#include <stdint.h>
#include <stdio.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <pthread.h>
#include "global.h"
#include "ietPiLib.h"
#include "prt_log.h"

#define LIB_VERSION(a,b,c)                  ((a << 24) + (b << 16) + (c))

#define PI_MAX_CHANNEL      16              /* PI 最大通道数 */
#if (HARD_VER == 0)
#define PI_PIM_CHANNEL      4               /* PI 在 PI 模块中通道数 */
#define PI_ACM_CHANNEL      2               /* PI 在 AC 模块中通道数 */
#elif (HARD_VER == 1)
#define PI_PIM_CHANNEL      8               /* PI 在 PI 模块中通道数 */
#define PI_ACM_CHANNEL      2               /* PI 在 AC 模块中通道数 */
#endif

#define PI_DRV_NAME         "pcie_pi"       /* PI 驱动名称 */
#define PI_CYCLE_NS_2_S     1000000000.0    /* PI 周期 ns -> s */
#define PI_MAX_CYCLE        0xFFFFFFFF      /* PI 最大周期值 */

/* IOCTL 命令定义 */
#define IOCTL_PI_MAGIC                      'P'                                         /* 标识设备驱动 */
#define IOCTL_PI_SET_EDGE                   _IOW(IOCTL_PI_MAGIC, 1, uint8_t)            /* 配置 PI 边沿 */
#define IOCTL_PI_SET_FILTER                 _IOW(IOCTL_PI_MAGIC, 2, uint16_t)           /* 配置 PI 滤波时间 */

#define IOCTL_PI_GET_DRV_VERSION            _IOR(IOCTL_PI_MAGIC, 101, uint32_t)         /* 获取驱动版本号 */
#define IOCTL_PI_GET_FPGA_VERSION           _IOR(IOCTL_PI_MAGIC, 102, uint32_t)         /* 获取 FPGA 版本号 */

/* 应用层 */
enum PiEdge {
    PI_EDGE_FALL            = 0,            /* 下降沿 */
    PI_EDGE_RISE            = 1,            /* 上升沿 */
};

/* 驱动层*/
enum PiDevEdge {
    /* 硬件为反逻辑，须软件反转边沿寄存器电平 */
    PI_DEV_EDGE_RISE        = 1,            /* 上升沿 */
    PI_DEV_EDGE_FALL        = 0,            /* 下降沿 */
};

struct PiDev_t {
    void *parent;                                           /* 父指针 */
    char name[32];                                          /* 设备名 */
    uint8_t index;                                          /* 设备号 */

    uint8_t setEdge;                                        /* 边沿 */
    uint16_t setFilter;                                     /* 滤波时间, us */
    uint32_t getCycle;                                      /* 周期值, ns */

    int fd;                                                 /* 文件 */
    char path[PATH_MAX];                                    /* 路径 */
};
struct PiCtrl_t {
    char name[16];                                          /* 控制器名 */
    uint8_t num;                                            /* 设备数量 */
    uint8_t max_num;                                        /* 最大数量 */
    struct PiDev_t dev[PI_MAX_CHANNEL];                     /* 设备描述 */
    uint32_t drvVer;                                        /* 驱动 版本号 */
    uint32_t fpgaVer;                                       /* FPGA 版本号 */
    uint32_t libVer;                                        /* 动态库 版本号 */

    uint8_t type;                                           /* 模块类型 */
};
/* PI 控制器 */
static struct PiCtrl_t PiCtrl;

/* 获取设备数量 */
static uint8_t PIMGetDevNum(int type)
{
    uint8_t num = 0;

    switch (type) {
    case IET_ECU:
    case IET_DI:
    case IET_DO:
    case IET_AI:
    case IET_AO:
        goto err_retrun;
    case IET_PI:
        num = PI_PIM_CHANNEL;
        break;
    case IET_SOE:
        goto err_retrun;
    case IET_AC:
        num = PI_ACM_CHANNEL;
        break;
    case IET_PT100:
    case IET_IEPE:
        goto err_retrun;
    default:
        prt_erro("Invalid type %d for %s\n", type, __func__);
        return 0;
    }
    prt_info("%s get %d device\n", __func__, num);

    return num;

err_retrun:
    prt_erro("%s get no device\n", __func__);

    return 0;
};

/* 打开设备 */
int PIMOpen(int moduleType)
{
    struct PiCtrl_t *pctrl = &PiCtrl;
    struct PiDev_t *pdev = &pctrl->dev[0];
    int ret = 0;
    uint8_t i = 0;
    int8_t j = 0;

    pctrl->num = PIMGetDevNum(moduleType);
    pctrl->max_num = PI_MAX_CHANNEL;
    pctrl->type = (uint8_t)moduleType;
    /* 检查参数 */
    if (pctrl->num > pctrl->max_num) {
        prt_erro("Get PI number %d, max is %d\n", pctrl->num, pctrl->max_num);
        return -EFAULT;
    }
    /* 生成路径 */
    snprintf(pctrl->name, sizeof(pctrl->name), "%s", PI_DRV_NAME);
    for (i = 0; i < pctrl->num; i++) {
        pdev = &pctrl->dev[i];
        pdev->parent = pctrl;
        snprintf(pdev->name, sizeof(pdev->name), "%s%d", pctrl->name, i);
        pdev->index = i;
        pdev->fd = -ENODEV;
        ret = snprintf(pdev->path, PATH_MAX, "/dev/%s", pdev->name);
        if (ret < 0 || ret >= PATH_MAX) {
            prt_erro("Generate %s path failed\n", pdev->name);
            return -ENAMETOOLONG;
        }
        /* 打开设备 */
        pdev->fd = open(pdev->path, O_RDWR);
        if (pdev->fd < 0) {
            ret = errno;
            prt_erro("Open %s device failed, %s\n", pdev->path, strerror(ret));
            ret = -ret;
            goto err_close_fd;
        }
        prt_dbug("Open %s device success\n", pdev->path);
    }

    pdev = &pctrl->dev[0];
    /* 获取驱动版本号 */
    ret = ioctl(pdev->fd, IOCTL_PI_GET_DRV_VERSION, &pctrl->drvVer);
    if (ret < 0) {
        prt_erro("Get PI driver version 0x%08x failed\n", pctrl->drvVer);
        goto err_close_fd;
    }
    prt_dbug("Get PI driver version 0x%08x\n", pctrl->drvVer);
    /* 获取 FPGA 版本号 */
    ret = ioctl(pdev->fd, IOCTL_PI_GET_FPGA_VERSION, &pctrl->fpgaVer);
    if (ret < 0) {
        prt_erro("Get PI FPGA version 0x%08x failed\n", pctrl->fpgaVer);
        goto err_close_fd;
    }
    prt_dbug("Get PI FPGA version 0x%08x\n", pctrl->fpgaVer);
    pctrl->libVer = LIB_VERSION(PI_VER_MAJOR, PI_VER_MINOR, PI_VER_PATCH);
    prt_dbug("Get PI library version 0x%08x\n", pctrl->libVer);

    return 0;

err_close_fd:
    for (j = i - 1; j >= 0; j--) {
        pdev = &pctrl->dev[j];
        close(pdev->fd);
        pdev->fd = -ENODEV;
    }

    return ret;
}

/* 初始化设备 */
int PIMInit(int channel, struct PimParam pimParam)
{
    struct PiCtrl_t *pctrl = &PiCtrl;
    struct PiDev_t *pdev = &pctrl->dev[0];
    int ret = 0;
    uint8_t i = 0;
    uint8_t start = 0;
    uint8_t end = 0;
    uint8_t edge = 0;

    /* 检查参数 */
    if (channel < 0 || 
        pimParam.window < 0 || pimParam.window > UINT16_MAX) {
        prt_erro("Invalid parameters for %s\n", __func__);
        return -EFAULT;
    }
    if (channel >= pctrl->num) {
        start = 0;
        end = pctrl->num;
    } else {
        start = channel;
        end = channel + 1;
    }
    /* 配置边沿 */
    switch (pimParam.type) {
    case PI_EDGE_FALL:
        edge = PI_DEV_EDGE_FALL;
        break;
    case PI_EDGE_RISE:
        edge = PI_DEV_EDGE_RISE;
        break;
    default:
        prt_erro("Invalid edge %d for %s\n", pimParam.type, __func__);
        return -EFAULT;
    }
    for (i = start; i < end; i++) {
        pdev = &pctrl->dev[i];
        pdev->setEdge = edge;
        pdev->setFilter = pimParam.window;
        /* 检查参数 */
        if (pdev->fd == 0) {
            prt_erro("Invalid %s->fd for %s\n", pdev->name, __func__);
            return -EFAULT;
        }
        /* 配置 PI 边沿 */
        ret = ioctl(pdev->fd, IOCTL_PI_SET_EDGE, &pdev->setEdge);
        if (ret < 0) {
            prt_erro("Set %s setEdge %d failed\n", pdev->name, pdev->setEdge);
            return ret;
        }
        prt_dbug("Set %s setEdge %d success\n", pdev->name, pdev->setEdge);
        /* 配置 PI 滤波 */
        ret = ioctl(pdev->fd, IOCTL_PI_SET_FILTER, &pdev->setFilter);
        if (ret < 0) {
            prt_erro("Set %s filter %dus failed\n", pdev->name, pdev->setFilter);
            return ret;
        }
        prt_dbug("Set %s filter %dus success\n", pdev->name, pdev->setFilter);
    }

    return ret;
}

int PIMReadReal(int channel, double *data)
{
    struct PiCtrl_t *pctrl = &PiCtrl;
    struct PiDev_t *pdev = &pctrl->dev[0];
    int ret = 0;

    /* 检查参数 */
    if (!data || 
        channel < 0 || channel >= pctrl->num) {
        prt_erro("Invalid parameters for %s\n", __func__);
        return -EFAULT;
    }
    pdev = &pctrl->dev[channel];
    /* 检查参数 */
    if (pdev->fd == 0) {
        prt_erro("Invalid %s->fd for %s\n", pdev->name, __func__);
        return -EFAULT;
    }
    /* 获取 PI 数值 */
    ret = read(pdev->fd, &pdev->getCycle, sizeof(pdev->getCycle));
    if (ret < 0) {
        prt_erro("Get PI%d freq failed\n", pdev->index);
        return -EFAULT;
    }
    if (pdev->getCycle == PI_MAX_CYCLE) {
        *data = 0;
    } else {
        *data = 1.0 / (pdev->getCycle / PI_CYCLE_NS_2_S);
    }
    prt_info("Get PI%d cycle %uns\n", pdev->index, pdev->getCycle);
    prt_info("Get PI%d freq %.6lfHz\n", pdev->index, *data);

    return 0;
}

int PIMClose()
{
    struct PiCtrl_t *pctrl = &PiCtrl;
    struct PiDev_t *pdev = &pctrl->dev[0];
    int ret = 0;
    uint8_t i = 0;

    for (i = 0; i < pctrl->num; i++) {
        pdev = &pctrl->dev[i];
        /* 检查参数 */
        if (pdev->fd == 0) {
            prt_erro("Invalid %s->fd for %s\n", pdev->name, __func__);
            return -EFAULT;
        }
        /* 关闭设备 */
        ret = close(pdev->fd);
        if (ret != 0) {
            prt_erro("Close %s failed\n", pdev->path);
            return -EFAULT;
        }
        pdev->fd = 0;
        prt_dbug("Close %s success\n", pdev->path);
    }

    return 0;
}

/* 获取驱动版本号 */
int PIMGetDrvVer()
{
    struct PiCtrl_t *pctrl = &PiCtrl;

    prt_dbug("%s %d.%d.%d\n", __func__, ((pctrl->drvVer >> 24) & 0xFF), 
             ((pctrl->drvVer >> 16) & 0xFF), (pctrl->drvVer & 0xFFFF));

    return (int)(pctrl->drvVer >> 16);
}

/* 获取 FPGA 版本号 */
int PIMGetFpgaVer()
{
    struct PiCtrl_t *pctrl = &PiCtrl;

    prt_dbug("%s %d.%d\n", __func__, ((pctrl->fpgaVer >> 8) & 0xFF), 
             (pctrl->fpgaVer & 0xFF));

    return (int)(pctrl->fpgaVer);
}

/* 获取动态库版本号 */
int PIMGetLibVer()
{
    struct PiCtrl_t *pctrl = &PiCtrl;

    prt_dbug("%s %d.%d.%d\n", __func__, ((pctrl->libVer >> 24) & 0xFF), 
             ((pctrl->libVer >> 16) & 0xFF), (pctrl->libVer & 0xFFFF));

    return (int)(pctrl->libVer >> 16);
}
